package com.rpinfo.editalog.venda;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;

/**
 *
 * @author camilo
 */
public class FinTableModel extends AbstractTableModel {

  private static final long serialVersionUID = 1L;
  public static final int COLUNA_QTD = Fin.class.getDeclaredFields().length;
  public static List<String> COLUNAS_NAME = new ArrayList<>();

  /**
   * Essa lista armazenará os objetos atualmente exibidos na tabela.
   */
  private List<Fin> lstMaster;
  private TableRowSorter<FinTableModel> sorter;

  /**
   * Constutor que simplesmente instancia o nosso {@link List} que usaremos para
   * guardar os valores.
   */
  public FinTableModel() {
    // no construtor, instanciamos o List  
    lstMaster = new ArrayList<>();

    for (Field declaredField : Fin.class.getDeclaredFields()) {
      COLUNAS_NAME.add(declaredField.getName());
    }
  }

  /**
   * Criamos um construtor de conveniência para já popular a lista.
   *
   * @param lista a lista de produtos a ser adicionada.
   */
  public FinTableModel(List<Fin> lista) {
    this();
    lstMaster.addAll(lista);
  }

  // ==============================================================  
  // Métodos implementados.  
  // ==============================================================  
  /**
   * Aqui configura-se o tipo de dados que serão apresentados por coluna
   *
   * Use o código abaixo para definir todas as colunas com o mesmo tipo de
   * dados.<br /><br />
   * <i>return "TIPO".class;</i>
   *
   * @param coluna
   * @return
   */
  @Override
  public Class<?> getColumnClass(int coluna) {
    List<Class> fields = new ArrayList<>();
    for (Field f : Fin.class.getDeclaredFields()) {
      fields.add(f.getType());
    }
    return fields.get(coluna);
  }

  /**
   * Retorna a quantidade de colunas da tabela.
   *
   * @return
   */
  @Override
  public int getColumnCount() {
    return COLUNA_QTD;
  }

  /**
   * Retorna o nome de cada coluna
   *
   * @param coluna
   * @return
   */
  @Override
  public String getColumnName(int coluna) {
    return COLUNAS_NAME.get(coluna);
  }

  /**
   * Retorna a quantidade de linhas da tabela.
   *
   * @return
   */
  @Override
  public int getRowCount() {
    return lstMaster.size();
  }

  /**
   * Retorna o valor de determinada célula.
   *
   * @param linha
   * @param coluna
   * @return
   */
  @Override
  public Object getValueAt(int linha, int coluna) {
//    Cria-se um objeto com base na linha solicitada
    Fin obj = lstMaster.get(linha);
    Object retorno = (Object) null;
    try {
      String objs = COLUNAS_NAME.get(coluna).toLowerCase();

      for (int i = 0; i < obj.getClass().getMethods().length; i++) {
        Method m = obj.getClass().getMethods()[i];
        if ((m.getName().startsWith("get") || m.getName().startsWith("is"))
                && (("get" + objs).equalsIgnoreCase(m.getName()) || ("is" + objs).equalsIgnoreCase(m.getName()))) {
          retorno = m.invoke(obj, null);
        }
      }
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
      ex.printStackTrace();
      return retorno;
    }
    return retorno;
  }

  /**
   * Define as células que poderão ser editadas.
   *
   * @param rowIndex
   * @param columnIndex
   * @return
   */
  @Override
  public boolean isCellEditable(int rowIndex, int columnIndex) {
    // nesse caso, todas as células são editáveis  
    return false;
  }

  /**
   * Define o valor para a célula da tabela
   *
   * @param valor - Novo valor para a célula
   * @param linha - Linha onde está a célula a ser alterada
   * @param coluna Coluna onde está a célula a ser alterada
   */
  @Override
  public void setValueAt(Object valor, int linha, int coluna) {
//    Cria-se um objeto com base na linha solicitada
    Fin obj = lstMaster.get(linha);
    try {
      String objs = COLUNAS_NAME.get(coluna).toLowerCase();

      for (int i = 0; i < obj.getClass().getMethods().length; i++) {
        Method m = obj.getClass().getMethods()[i];
        if (m.getName().startsWith("set") && ("set" + objs).equalsIgnoreCase(m.getName())) {
          m.invoke(obj, valor.toString());
        }
      }
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
      ex.printStackTrace();
    }

    // é importante notificar os listeners a cada alteração  
    fireTableDataChanged();
  }

  /**
   * Recebe o índice do a ser removido. Para recuperar o índice, use o método
   * {@link #getIndice()} antes.
   *
   * @param indice
   */
  public void remove(int indice) {
    lstMaster.remove(indice);
    fireTableRowsDeleted(indice, indice);
  }

  /**
   * Retorna o índice de determinado registro.
   *
   * @param m
   * @return
   */
  public int getIndice(Fin m) {
    return lstMaster.indexOf(m);
  }

  /**
   * Adiciona todos os registros na lista à este modelo.
   *
   * @param lista
   */
  @SuppressWarnings("unchecked")
  public void adicionaLista(List<?> lista) {
    int i = getRowCount();
    lstMaster.addAll((List<Fin>) lista);
    fireTableRowsInserted(i, getRowCount() - 1);
  }

  /**
   * Adiciona um novo registro e notifica os listeners que um novo registro foi
   * adicionado.
   *
   * @param classe
   */
  public void adiciona(Fin classe) {
    lstMaster.add(classe);
    // informamos os listeners que a linha (size - 1) foi adicionada  
    fireTableRowsInserted(lstMaster.size() - 1, lstMaster.size() - 1);
  }

  /**
   * Esvazia a lista.
   */
  public void limpaLista() {
    int i = lstMaster.size();
    lstMaster.clear();
    fireTableRowsDeleted(0, i - 1);
  }

  public void sorter(JTable table, FinTableModel model) {
    sorter = new TableRowSorter<>(model);
    table.setRowSorter(sorter);
  }

}
